/**
 * @fileOverview Fight related classes
 * @requires app
 */

 (function() {
	/**
	 * Class that utilizes fight procedures
	 *
	 * @class Fight class
	 * @extends app.Basic
	 *
	 * @property {Number[]} timers Array of timers for current battle
	 * @property {app.UnitGroup[]} groups Array of fighting groups
	 */
	app.Fight = app.Basic.extend({
		/** @lends app.Fight.prototype */
		
		timers : [],
		groups : [],
		
		/**
		 * Start new fight sequence.<br />Will trigger {@link #event:start}
		 *
		 * @params {Object} [options] Battle configuration
		 * @param {app.UnitGroup} options.attacker Attacker group
		 * @param {app.UnitGroup} options.target Target group
		 */
		init : function(options) {
			options || (options = {});
			
			this.groups = [options.attacker, options.target];
		},
		
		/**
		 * Start fight
		 *
		 * @returns {app.Fight} Current fight instance
		 */
		start : function() {
			this.trigger('start', this.groups);
			app.debug('Start fight');
			
			for (var i = 0; i < this.groups.length; i++) {
				for (var j = 0; j < this.groups[i].size(); j++) {
					this.setTimer({
						unit : this.groups[i].get(j),
						group : i
					});
				}
			}
			
			return this;
		},
		
		/**
		 * Set the timer for unit
		 *
		 * @param {Object} options Timer options
		 * @param {app.Unit} options.unit Unit
		 * @param {Number}  options.group Group ID
		 */
		setTimer : function(options) {
			var unit = options.unit,
				groupID = options.group,
				that = this,
				timer;
		
			timer = setInterval(function() {
				that.doAction(unit, groupID);
			}, 1000 - (unit.getAttr('agility') * 100));
			
			this.timers.push(timer);
		},
		
		/**
		 * Do the next action.<br>Will trigger {@link #event:skillused}, {@link #event:restoreused}, {@link #event:hit}, {@link #event:unitdead}
		 *
		 * @param {app.Unit} unit Unit which will do actions
		 * @param {Number} groupID Group ID to which this units belongs
		 */
		doAction : function(unit, groupID) {
			var target,
				group,
				skill,
				affect = {},
				damage = 0,
				restoration,
				effect,
				exp;
			
			// Skill or damage
			if (skill = unit.getAction()) {
				for (var i = 0; i < skill.effects.length; i++) {
					group = skill.effects[i].target == app.Effect.TARGET_ENEMY ? (1 - groupID) : groupID;
					target = skill.effects[i].group ? this.groups[group] : this.groups[group].getRandom();
					
					if (skill.effects[i].target == app.Effect.TARGET_SELF) {
						target = unit;
					}
					
					switch (skill.effects[i].type) {
						case app.Effect.TYPE_BUFF:
							if (skill.effects[i].duration == 0) {
								effect = skill.effects[i].getAmount();
							}
							else {
								effect = skill.effects[i];
							}
							
							break;
						case app.Effect.TYPE_DEBUFF:
							effect = skill.effects[i];
							
							break;
						default:
							if (skill.effects[i].duration == 0) {
								effect = skill.effects[i].getAmount();
							}
							else {
								effect = skill.effects[i];
							}
					}
				}
				
				this.trigger('skillused', unit, target, skill);
				app.debug(unit + ' used ' + skill + ' on ' + target);
			}
			else {
				target = this.groups[group].getRandom();
			
				effect = {
					attribute : 'health',
					amount : unit.getAttack(),
					positive : false
				};
			}
			
			if (effect) {
				if (effect instanceof app.Effect) {
					target.addEffect(effect);
				}
				else {
					target.modifyAttr(effect.attribute, effect.amount * (effect.positive ? 1 : -1));
					
					this.trigger(effect.positive ? 'restoreused' : 'hit', unit, target, effect.amount, effect.attribute);
					app.debug(effect.positive ? (unit + ' restored ' + effect.amount + ' of ' + effect.attribute + ' to ' + target) : (unit + ' hit ' + target + ' for ' + effect.amount + ' ' + effect.attribute + ' damage'));
				}
			}
			
			if (target.isDead()) {
				exp = Math.round(target.exp.next / 10);
				
				this.trigger('unitdead', target);
				app.debug(target + ' is dead');
				
				unit.addExp(exp);
			}
			
			this.checkComplete();
		},
		
		/**
		 * Check if fight is completed.<br>Will trigger {@link #event:complete}
		 */
		checkComplete : function() {
			if (this.groups[0].isDead() || this.groups[1].isDead()) {
				this.trigger('complete');
				app.debug('Battle completed');
				
				this.resetTimers();
			}
		},
		
		/**
		 * Reset all fight timers
		 */
		resetTimers : function() {
			for (var i = 0; i < this.timers.length; i++) {
				clearInterval(this.timers[i]);
			}
		}
	});
	
	/**
	 * Fired at the beginning on each fight
	 *
	 * @name app.Fight#start
	 * @event
	 * @param {Array} groups Array of group that are fighting
	 */
	
	/**
	 * Fired when unit uses a skill
	 *
	 * @name app.Fight#skillused
	 * @event
	 * @param {app.Unit} unit Unit that used skill
	 * @param {app.Unit|app.UnitGroup} target Target to which skill will be used
	 * @param {app.Skill} skill Skill which will be used
	 */
	
	/**
	 * Fired when unit applies restoration effect to target
	 *
	 * @name app.Fight#restoreused
	 * @event
	 * @param {app.Unit} unit Unit that used restoration effect
	 * @param {app.Unit} target Unit to which restoration effect is applied
	 * @param {Number|String} amount Amount of effect applied
	 * @param {String} attribute Attribute which will be affected by effect
	 */
	
	/**
	 * Fired when unit hit another unit
	 *
	 * @name app.Fight#hit
	 * @event
	 * @param {app.Unit} unit Unit that used restoration effect
	 * @param {app.Unit} target Unit to which restoration effect is applied
	 * @param {Number|String} amount Amount of effect applied
	 * @param {String} attribute Attribute which will be affected by effect
	 */
	
	/**
	 * Fired when unit is dead
	 *
	 * @name app.Fight#unitdead
	 * @event
	 * @param {app.Unit} unit Unit which is dead
	 */
	
	/**
	 * Fired when battle finished
	 *
	 * @name app.Fight#complete
	 * @event
	 */
})();